const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

class MyPromise {
    constructor (callback) {
        /** 
        * 利用try catch 来捕获错误，如果报错了就直接调用reject把错误反出去 
        */
        try {
            callback(this.resolve, this.reject);
        } catch (err) {
            this.reject(err)
        }
    }
    status = PENDING; // 状态默认是pending；

    // 保存值
    successVal = undefined;
    failVal = undefined;

    // 保存成功失败的函数
    successCallback = [];
    failCallback = [];
    /**
     * 这里resolve 和 reject 使用箭头函数是为了this指向上下文MyPromise,
     * function也可以，但是需要改变this指向
     */
    resolve = (successVal) => {
        // resolve和reject只能从pending -> resolve, pending -> reject
        if (this.status !== PENDING) return;
        this.status = FULFILLED;
        this.successVal = successVal;
        while(this.successCallback.length) this.successCallback.shift()();
    }

    reject = (failVal) => {
        if (this.status !== PENDING) return;
        this.status = REJECTED;
        this.failVal = failVal;
        while(this.successCallback.length) this.successCallback.shift()();
    }

    then (successCallback, failCallback) {
        /**
         * .then 方法是可以链式调用的，比如 .then().then().then(val => console.log(val))，
         * 这个时候就需要把没有做操作的then中的值return给下一个then
         */
        successCallback = successCallback ? successCallback : successVal => successVal;
        failCallback = failCallback ? failCallback : failVal => {throw failVal};

        // 由于promise的 .then 方法返回一个promise，所以这里也返回一个mypromise;
        let promiseBack = new MyPromise((resolve, reject) => {
            if (this.status === FULFILLED) {
                // 因为调用checkPromise的时候拿不到promiseBack， 所以调用seTTimeout 把代码变成异步的
                setTimeout(() => {
                    try {
                        let result = successCallback(this.successVal);
                        checkPromise(promiseBack, result, resolve, reject);  
                    } catch (e) {
                        reject(e);
                    }
                    
                }, 0);
            } else if (this.status === REJECTED) {
                setTimeout(() => {
                    try {
                        let result = failCallback(this.failVal);
                        checkPromise(promiseBack, result, resolve, reject);
                    } catch (e) {
                        reject(e);
                    }
                 
                }, 0);
            } else {
                // pending状态
                /**
                 * 当then被多次调用同时还携带异步操作的时候，就需要把成功和失败的函数存储起来
                 * 在resolve和reject的时候循环调用
                 */
                this.successCallback.push(() => {
                    setTimeout(() => {
                        try {
                            let result = successCallback(this.successVal);
                            checkPromise(promiseBack, result, resolve, reject);
                        } catch (e) {
                            reject(e);
                        }
                    }, 0);
                });
                this.failCallback.push(() => {
                    setTimeout(() => {
                        try {
                            let result = failCallback(this.failVal);
                            checkPromise(promiseBack, result, resolve, reject);
                        } catch (e) {
                            reject(e);
                        }
                    }, 0)
                });
            }
        });

        return promiseBack;
    }

    /**
     * catch 是可以在then后边链式调用的并且返回一个promise， then自己也可以链式调用并且返回一个promise
     * 这里借助then直接调用，返回rejecct
     */
    catch(failCallback) {
        return this.then(undefined, failCallback);
    }

    // finally 不管对错都会给一个结果，而且后面可以链式调用then， 所以返回一个promise
    finally(finallyCallback) {
        return this.then(val => {
            return MyPromise.resolve(finallyCallback()).then(() => val);
        }, err => {
            return MyPromise.resolve(finallyCallback()).then(() => {throw err});
        })
    }
 
    // all 是需要promise.all调用的  所以这里需要写在static上
    static all(arr) {
        return new MyPromise((resolve, reject) => {
            let result = []; // 用来存放all传进来的promise或普通值;
            let index = 0; // 

            for (let i = 0; i < arr.length; i++) {
                let current = arr[i];
                // 这里需要判断all方中传进来的每一个值是普通值还是promise
                if (current instanceof MyPromise) {
                    /**
                     * 1、是promise就需要拿到值，并把值存进去，如果在拿值的过程中报错了就直接报错；
                     * 2、all方法的特点是全部返回才会返回，有一个没返回就挂掉，所以用index来计数
                     */
                    current.then(val => add(i, val), err => reject(err));
                } else {
                    add(i, current);
                }
            }

            /**
             * 一个辅助函数，因为被多次运用到，抽离出来
             * 用来存放all方法中传进来的值
             */
            function add (key, value) {
                result[key] = value;
                index++; // 没存一个index就++

                if (arr.length === index) {
                    // 如果都返回了，就resolve出去
                    resolve(result);
                }
            }

        })
    }

    // resolve 是需要promise.resolve调用的  所以这里需要写在static上
    static resolve(val) {
        /**
         * resolve就是返回一个成功的promise，所以需要判断val是不是一个promise
         * 是 直接return
         * 否 创建一个promise并且把值resolve出去
         */
        if (val instanceof MyPromise) {
            return val;
        }

        return new MyPromise((resolve, reject) => resolve(val));
    }
}
/**
 * 1.一个辅助函数，有来判断then中返回的是不是一个promise，如果是就调用.then方法拿到值，然后resolve出去
 * 如果不是，就直接resolve出去
 * 2.判断是否出现了promise循环调用的情况，如果是就报错循环调用了
 */
function checkPromise(promise, result, resolve, reject) {
    // 判断回调的返回是不是和当前then的返回是一样的promise，如果是就是循环调用了
    if (promise === result) {
        return reject(new TypeError('promise 被循环调用了'));
    }
    // 判断回调的返回是不是一个promise，如果是就then到它的值然后传出去
    if (result instanceof MyPromise) {
        result.then(resolve, reject);
    } else {
        resolve(result);
    }
}
